// $Id: model.hpp 2 2009-09-02 08:47:42Z joseph2002 $
/*
 * Copyright 2009 Joseph Wu
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 *
 * For more information, see http://xpcomplus.sourceforge.net
 */

#ifndef XPGEN_MODEL_HPP
#define XPGEN_MODEL_HPP

#include <string>
#include <vector>
#include <boost/cstdint.hpp>

namespace xpgen {

struct module_decl;
struct interface_decl;
struct constant_decl;
struct method_decl;
struct param_decl;
struct type_decl;

// uuid
struct uuid
{
    boost::uint32_t m0;
    boost::uint16_t m1;
    boost::uint16_t m2;
    boost::uint8_t  m3[8];

    std::string to_str() const;
    std::string to_value_str() const;
};

// type_decl
struct type_decl
{
    enum tags
    {
        tag_void                = 0,

        tag_int8                = 1,
        tag_int16               = 2,
        tag_int32               = 3,
        tag_int64               = 4,
        tag_uint8               = 5,
        tag_uint16              = 6,
        tag_uint32              = 7,
        tag_uint64              = 8,
        tag_float               = 9,
        tag_double              = 10,
        tag_bool                = 11,
        tag_char                = 12,
        tag_wchar               = 13,

        tag_uuid                = 14,
        tag_uuid_ref            = 15,
        tag_uuid_ptr            = 16,

        tag_string              = 17,
        tag_wstring             = 18,
        tag_utf8string          = 19,
        tag_domstring           = 20,
        tag_string_ptr          = 21,
        tag_wstring_ptr         = 22,
        tag_utf8string_ptr      = 23,
        tag_domstring_ptr       = 24,

        tag_pstring             = 25,
        tag_pwstring            = 26,

        tag_interface           = 27,
        tag_interface_qi        = 28,
        tag_interface_qi_out    = 29,

        tag_native              = 30,

        // Extension types
        tag_result              = 31,
        tag_size                = 32,

        tag_max
    };

    boost::uint16_t tag;
    bool is_array;
    interface_decl* itf;

    type_decl();
    ~type_decl();

    bool is_void() const
    {
        return tag == tag_void /*&& !is_array*/;
    }

    bool is_standard() const
    {
        return tag >= tag_int8 && tag <= tag_wchar;
    }

    bool is_uuid() const
    {
        return tag >= tag_uuid && tag <= tag_uuid_ptr;
    }

    bool is_string() const
    {
        return tag >= tag_string && tag <= tag_domstring;
    }

    bool is_string_ptr() const
    {
        return tag >= tag_string_ptr && tag <= tag_domstring_ptr;
    }

    bool is_pstring() const
    {
        return tag == tag_pstring || tag == tag_pwstring;
    }

    bool is_interface() const
    {
        return tag >= tag_interface && tag <= tag_interface_qi_out;
    }

    bool is_dipper() const
    {
        // *string and *string_ptr
        return tag >= tag_string && tag <= tag_domstring_ptr;
    }
};

// param_decl
struct param_decl
{
    enum flags
    {
        flag_in         = 0x0001,
        flag_out        = 0x0002,
        flag_retval     = 0x0004,

        flag_shared     = 0x0010,
        flag_const      = 0x0020,
        flag_optional   = 0x0040,

        flag_primary    = 0x1000,
        flag_iid_is     = 0x2000,
        flag_size_is    = 0x4000,
        flag_length_is  = 0x8000
    };

    method_decl* method;

    std::string name;
    type_decl type;
    boost::uint32_t flags; // higher 16-bit are free for application to use
    union
    {
        param_decl* primary_; // for sub
        param_decl** primaries_; // for sub which has several primary params
        param_decl* iid_is; // for primary interface_qi
        param_decl* size_is; // for primary array|pstring
    };
    param_decl* length_is; // for primary array|pstring
    unsigned qi_index; // for primary interface_qi
    unsigned primary_count; // for sub

    void* user_data;

    explicit param_decl(method_decl* method);
    ~param_decl();

    std::string context_name() const;

    // Will return the primary param if there is only one, or the first one
    // if there are many
    // Always access primary param through this function
    param_decl* primary() const;

    // Return the n-th primary, for primary_count > 0
    // Assertion if primary_count <= 1
    param_decl* primary(unsigned n) const;

    // Add primary param use this function
    // Will fail if this param is primary parameter
    void add_primary(param_decl* primary);

    bool is_primary() const
    {
        return (flags & flag_primary) != 0;
    }

    bool is_sub() const
    {
        return (flags & (flag_iid_is | flag_size_is | flag_length_is)) != 0;
    }

    bool is_normal() const
    {
        return !is_primary() && !is_sub();
    }

    bool is_qi() const
    {
        return is_primary() && type.tag == type_decl::tag_interface_qi;
    }

    bool is_iid_is() const
    {
        return (flags & flag_iid_is) != 0;
    }

    unsigned size_length_flag() const
    {
        return static_cast<unsigned>(flags & (flag_size_is | flag_length_is));
    }

    bool is_size_is() const
    {
        return size_length_flag() == flag_size_is;
    }

    bool is_length_is() const
    {
        return size_length_flag() == flag_length_is;
    }

    bool is_size_length_is() const
    {
        return size_length_flag() == (flag_size_is | flag_length_is);
    }

    bool has_size_is() const
    {
        return (flags & flag_size_is) != 0;
    }

    bool has_length_is() const
    {
        return (flags & flag_length_is) != 0;
    }

    bool is_shared() const
    {
        return (flags & flag_shared) != 0;
    }

    bool is_const() const
    {
        return (flags & flag_const) != 0;
    }

    bool is_optional() const
    {
        return (flags & flag_optional) != 0;
    }

    unsigned in_out_flag() const
    {
        return static_cast<unsigned>(flags & (flag_in | flag_out));
    }

    bool is_in() const
    {
        return in_out_flag() == flag_in;
    }

    bool is_out() const
    {
        return in_out_flag() == flag_out;
    }

    bool is_inout() const
    {
        return in_out_flag() == (flag_in | flag_out);
    }

    bool has_in() const
    {
        return (flags & flag_in) != 0;
    }

    bool has_out() const
    {
        return (flags & flag_out) != 0;
    }

    bool is_retval() const
    {
        return (flags & flag_retval) != 0;
    }
};

// constant_decl
union constant_value
{
    boost::int16_t  i16;
    boost::uint16_t ui16;
    boost::int32_t  i32;
    boost::uint32_t ui32;
};

struct constant_decl
{
    interface_decl* itf;

    std::string name;
    type_decl type;
    constant_value value;

    void* user_data;

    explicit constant_decl(interface_decl* itf);
    ~constant_decl();

    std::string context_name() const;
};

// method_decl
struct method_decl
{
    enum flags
    {
        flag_hidden     = 0x0001,
        flag_notxpcom   = 0x0002,
        flag_getter     = 0x0004,
        flag_setter     = 0x0008,
        flag_ctor       = 0x0010
    };

    interface_decl* itf;

    std::string name;
    param_decl* result;
    std::vector<param_decl*> params;
    boost::uint32_t flags; // higher 16-bit are free for application to use
    unsigned qi_count;

    void* user_data;

    explicit method_decl(interface_decl* itf);
    ~method_decl();

    std::string context_name() const;

    bool is_hidden() const
    {
        return (flags & flag_hidden) != 0;
    }

    bool is_notxpcom() const
    {
        return (flags & flag_notxpcom) != 0;
    }

    bool is_scriptable() const;

    bool is_getter() const
    {
        return (flags & flag_getter) != 0;
    }

    bool is_setter() const
    {
        return (flags & flag_setter) != 0;
    }

    bool is_attribute() const
    {
        return is_getter() || is_setter();
    }

    bool is_ctor() const
    {
        return (flags & flag_ctor) != 0;
    }

    bool has_qi() const
    {
        return qi_count > 0;
    }

    bool is_result_retval_param() const
    {
        return !is_notxpcom() && !result->type.is_void();
    }

    bool is_result_void() const
    {
        return is_notxpcom() && result->type.is_void();
    }
};

// interface_decl
struct interface_decl
{
    enum flags
    {
        flag_root       = 0x1000,
        flag_resolved   = 0x2000,

        flag_scriptable = 0x0001,
        flag_function   = 0x0002
    };

    module_decl* module;
    interface_decl* base;
    std::vector<interface_decl*> references;

    std::string name;
    std::string ns;
    std::string ns_cpp;
    std::vector<std::string> ns_list;
    std::string qname;
    std::string qname_cpp;

    uuid iid;
    std::string iid_str;
    std::string iid_value_str;

    boost::uint32_t flags; // higher 16-bit are free for application to use

    std::vector<constant_decl*> constants;
    std::vector<method_decl*> methods;

    void* user_data;

    explicit interface_decl(module_decl* module);
    ~interface_decl();

    std::string context_name() const;

    bool is_base_of(const interface_decl* rhs) const;
    bool is_ancestor_of(const interface_decl* rhs) const;

    void set_base(interface_decl* base);
    void set_name(const std::string& name, const std::string& ns);
    void set_iid(const uuid& iid);

    bool is_root() const
    {
        return (flags & flag_root) != 0;
    }

    bool is_resolved() const
    {
        return (flags & flag_resolved) != 0;
    }

    bool is_scriptable() const
    {
        return (flags & flag_scriptable) != 0;
    }

    bool is_function() const
    {
        return (flags & flag_function) != 0;
    }
};

// module_decl
struct module_decl
{
    std::vector<interface_decl*> interfaces;

    void* user_data;

    module_decl();
    ~module_decl();

    interface_decl* root_interface() const
    {
        return interfaces[0];
    }
};

} // namespace xpgen

#endif // XPGEN_MODEL_HPP
